﻿// -----------------------------------------------------------------------
// <copyright file="StringExtensions.cs" company="Henadzi Sabaleuski.">
// Copyright (c) Henadzi Sabaleuski. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

namespace HSabaleuski.Utilities.DataTypes.ExtensionMethods
{
	#region Usings

	using System;
	using System.Linq;
	using System.Text;
	using System.Text.RegularExpressions;

	#endregion

	/// <summary>
	/// String extensions
	/// </summary>
	public static class StringExtensions
	{
		#region Encoding

		/// <summary>
		/// Converts a string to a string of another encoding
		/// </summary>
		/// <param name="input">input string</param>
		/// <returns>
		/// string of the byte array
		/// </returns>
		public static string Encode(this string input)
		{
			if (string.IsNullOrEmpty(input))
			{
				return string.Empty;
			}

			return input.Encode(null, null);
		}

		/// <summary>
		/// Converts a string to a string of another encoding
		/// </summary>
		/// <param name="input">input string</param>
		/// <param name="originalEncoding">The type of encoding the string is currently using (defaults to ASCII)</param>
		/// <returns>
		/// string of the byte array
		/// </returns>
		public static string Encode(this string input, Encoding originalEncoding)
		{
			if (string.IsNullOrEmpty(input))
			{
				return string.Empty;
			}

			return input.Encode(originalEncoding);
		}

		/// <summary>
		/// Converts a string to a string of another encoding
		/// </summary>
		/// <param name="input">input string</param>
		/// <param name="originalEncoding">The type of encoding the string is currently using (defaults to ASCII)</param>
		/// <param name="usingEncoding">The type of encoding the string is converted into (defaults to UTF8)</param>
		/// <returns>
		/// string of the byte array
		/// </returns>
		public static string Encode(this string input, Encoding originalEncoding, Encoding usingEncoding)
		{
			if (string.IsNullOrEmpty(input))
			{
				return string.Empty;
			}

			originalEncoding = originalEncoding ?? new ASCIIEncoding();
			usingEncoding = usingEncoding ?? new UTF8Encoding();

			return Encoding.Convert(originalEncoding, usingEncoding, input.ToByteArray(originalEncoding)).ToEncodedString(usingEncoding);
		}

		#endregion

		#region Converters

		/// <summary>
		/// Converts base 64 string based on the encoding passed in
		/// </summary>
		/// <param name="input">Input string</param>
		/// <param name="usingEncoding">The type of encoding the string is using (defaults to UTF8)</param>
		/// <returns>
		/// string in the encoding format
		/// </returns>
		public static string FromBase64(this string input, Encoding usingEncoding)
		{
			if (string.IsNullOrEmpty(input))
			{
				return string.Empty;
			}

			byte[] tempArray = Convert.FromBase64String(input);
			usingEncoding = usingEncoding ?? new UTF8Encoding();
			return usingEncoding.GetString(tempArray);
		}

		/// <summary>
		/// Converts base 64 string to a byte array
		/// </summary>
		/// <param name="input">Input string</param>
		/// <returns>
		/// A byte array equivalent of the base 64 string
		/// </returns>
		public static byte[] FromBase64(this string input)
		{
			return string.IsNullOrEmpty(input) ? default(byte[]) : Convert.FromBase64String(input);
		}

		/// <summary>
		/// Converts from the specified encoding to a base 64 string
		/// </summary>
		/// <param name="input">Input string</param>
		/// <returns>
		/// Bas64 string
		/// </returns>
		public static string ToBase64(this string input)
		{
			if (string.IsNullOrEmpty(input))
			{
				return string.Empty;
			}

			return input.ToBase64(null);
		}

		/// <summary>
		/// Converts from the specified encoding to a base 64 string
		/// </summary>
		/// <param name="input">Input string</param>
		/// <param name="originalEncoding">The type of encoding the string is using (defaults to UTF8)</param>
		/// <returns>Bas64 string</returns>
		public static string ToBase64(this string input, Encoding originalEncoding)
		{
			if (string.IsNullOrEmpty(input))
			{
				return string.Empty;
			}

			originalEncoding = originalEncoding ?? new UTF8Encoding();

			byte[] tempArray = originalEncoding.GetBytes(input);

			return Convert.ToBase64String(tempArray);
		}

		/// <summary>
		/// Converts a string to a byte array
		/// </summary>
		/// <param name="input">input string</param>
		/// <returns>
		/// the byte array representing the string
		/// </returns>
		public static byte[] ToByteArray(this string input)
		{
			if (string.IsNullOrEmpty(input))
			{
				return null;
			}

			return input.ToByteArray();
		}

		/// <summary>
		/// Converts a string to a byte array
		/// </summary>
		/// <param name="input">input string</param>
		/// <param name="usingEncoding">The type of encoding the string is using (defaults to UTF8)</param>
		/// <returns>the byte array representing the string</returns>
		public static byte[] ToByteArray(this string input, Encoding usingEncoding)
		{
			usingEncoding = usingEncoding ?? new UTF8Encoding();
			return string.IsNullOrEmpty(input) ? null : usingEncoding.GetBytes(input);
		}

		#endregion

		#region Formatting

		/// <summary>
		/// Gets the first x number of characters from the left hand side
		/// </summary>
		/// <param name="input">Input string</param>
		/// <param name="length">x number of characters to return</param>
		/// <returns>The resulting string</returns>
		public static string Left(this string input, int length)
		{
			return string.IsNullOrEmpty(input) ? string.Empty : input.Substring(0, input.Length > length ? length : input.Length);
		}

		/// <summary>
		/// Gets the last x number of characters from the right hand side
		/// </summary>
		/// <param name="input">Input string</param>
		/// <param name="length">x number of characters to return</param>
		/// <returns>
		/// The resulting string
		/// </returns>
		public static string Right(this string input, int length)
		{
			if (string.IsNullOrEmpty(input))
			{
				return string.Empty;
			}

			length = input.Length > length ? length : input.Length;

			return input.Substring(input.Length - length, length);
		}

		/// <summary>
		/// Reverses a string
		/// </summary>
		/// <param name="input">Input string</param>
		/// <returns>The reverse of the input string</returns>
		public static string Reverse(this string input)
		{
			return new string(input.Reverse<char>().ToArray());
		}

		/// <summary>
		/// Removes the filter text from the input.
		/// </summary>
		/// <param name="input">Input text</param>
		/// <param name="textForRemove">Regex expression of text to filter out</param>
		/// <returns>The input text minus the filter text.</returns>
		public static string RemoveTextExpression(this string input, string textForRemove)
		{
			if (string.IsNullOrEmpty(input))
			{
				return string.Empty;
			}

			return string.IsNullOrEmpty(textForRemove) ? input : new Regex(textForRemove).Replace(input, string.Empty);
		}

		/// <summary>
		/// Removes everything that is not in the filter text from the input.
		/// </summary>
		/// <param name="input">Input text</param>
		/// <param name="textForKeep">Regex expression of text to keep</param>
		/// <returns>
		/// The input text minus everything not in the filter text.
		/// </returns>
		public static string KeepTextExpression(this string input, string textForKeep)
		{
			if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(textForKeep))
			{
				return string.Empty;
			}

			Regex regex = new Regex(textForKeep);

			MatchCollection collection = regex.Matches(input);

			StringBuilder resultBuilder = new StringBuilder();
			foreach (Match match in collection)
			{
				resultBuilder.Append(match.Value);
			}

			return resultBuilder.ToString();
		}

		/// <summary>
		/// Keeps only alphanumeric characters
		/// </summary>
		/// <param name="input">Input string</param>
		/// <returns>the string only containing alphanumeric characters</returns>
		public static string KeepAlphanumeric(this string input)
		{
			return input.KeepTextExpression("[a-zA-Z0-9]");
		}

		/// <summary>
		/// Keeps only alpha characters
		/// </summary>
		/// <param name="input">Input string</param>
		/// <returns>the string only containing alpha characters</returns>
		public static string KeepAlphaCharacters(this string input)
		{
			return input.KeepTextExpression("[a-zA-Z]");
		}

		/// <summary>
		/// Keeps only numeric characters
		/// </summary>
		/// <param name="input">Input string</param>
		/// <returns>
		/// the string only containing numeric characters
		/// </returns>
		public static string KeepNumeric(this string input)
		{
			return input.KeepNumeric(false);
		}

		/// <summary>
		/// Keeps only numeric characters
		/// </summary>
		/// <param name="input">Input string</param>
		/// <param name="keepNumericPunctuation">Determines if decimal places should be kept</param>
		/// <returns>the string only containing numeric characters</returns>
		public static string KeepNumeric(this string input, bool keepNumericPunctuation)
		{
			string keepTextExpression = keepNumericPunctuation ? @"[0-9\.]" : "[0-9]";

			return input.KeepTextExpression(keepTextExpression);
		}

		/// <summary>
		/// Uses a regex to format the input string
		/// </summary>
		/// <param name="input">Input string</param>
		/// <param name="format">Regex string used to</param>
		/// <param name="outputFormat">Output format</param>
		/// <returns>
		/// The input string formatted by using the regex string
		/// </returns>
		public static string RegexFormat(this string input, string format, string outputFormat)
		{
			if (string.IsNullOrEmpty(input))
			{
				throw new ArgumentNullException("input");
			}

			return input.RegexFormat(format, outputFormat, RegexOptions.None);
		}

		/// <summary>
		/// Uses a regex to format the input string
		/// </summary>
		/// <param name="input">Input string</param>
		/// <param name="format">Regex string used to</param>
		/// <param name="outputFormat">Output format</param>
		/// <param name="options">Regex options</param>
		/// <returns>The input string formatted by using the regex string</returns>
		public static string RegexFormat(this string input, string format, string outputFormat, RegexOptions options)
		{
			if (string.IsNullOrEmpty(input))
			{
				throw new ArgumentNullException("input");
			}

			return Regex.Replace(input, format, outputFormat, options);
		}

		#endregion

		#region Helpers

		/// <summary>
		/// returns the number of times a string occurs within the text
		/// </summary>
		/// <param name="input">input text</param>
		/// <param name="match">The string to match (can be regex)</param>
		/// <returns>The number of times the string occurs</returns>
		public static int NumberTimesOccurs(this string input, string match)
		{
			return string.IsNullOrEmpty(input) ? 0 : new Regex(match).Matches(input).Count;
		}

		/// <summary>
		/// Determines if a string is unicode
		/// </summary>
		/// <param name="input">Input string</param>
		/// <returns>True if it's unicode, false otherwise</returns>
		public static bool IsUnicode(this string input)
		{
			return string.IsNullOrEmpty(input) ? true : Regex.Replace(input, @"[^\u0000-\u007F]", string.Empty) != input;
		}

		#endregion
	}
}